【エラー解消】 aws-mwaa-local-runner で cryptography.fernet.InvalidToken と言われたら
aws-mwaa-local-runner は Amazon Managed Workflows for Apache Airflow (MWAA) の環境をローカルで模倣するツールです。
中身はDockerイメージのビルド、コンテナの実行を行うシェルスクリプトになっています。
このツールを使用していたとき、 cryptography.fernet.InvalidToken
というエラーに遭遇したので、解消方法を紹介します。
環境
Docker version 20.10.17, build 100c701
Docker Desktop for Mac v4.12.0
aws-mwaa-local-runner v2.2.2
【問題】コネクション一覧を表示しようとすると cryptography.fernet.InvalidToken と言われる
READMEの通りに ./mwaa-local-env build-image
でDockerイメージをビルドし、./mwaa-local-env start
でコンテナを立ち上げて開発をしていました。
ウェブUIの [Admin] > [Connections] からコネクション一覧を表示しようとすると、以下のようにエラーが表示されました。
Something bad has happened. Airflow is used by many users, and it is very likely that others had similar problems and you can easily find a solution to your problem. Consider following these steps: * gather the relevant information (detailed logs with errors, reproduction steps, details of your deployment) * find similar issues using: * GitHub Discussions * GitHub Issues * Stack Overflow * the usual search engine you use on a daily basis * if you run Airflow on a Managed Service, consider opening an issue using the service support channels * if you tried and have difficulty with diagnosing and fixing the problem yourself, consider creating a bug report. Make sure however, to include all relevant details and results of your investigation so far. Python version: 3.7.10 Airflow version: 2.2.2 Node: redact ------------------------------------------------------------------------------- Error! Please contact server admin.
コンテナのログを見てみると、 cryptography.fernet.InvalidToken
と言われています。
aws-mwaa-local-runner-2_2-local-runner-1 | [2022-09-29 02:44:58,098] {{app.py:1892}} ERROR - Exception on /connection/list/ [GET] aws-mwaa-local-runner-2_2-local-runner-1 | Traceback (most recent call last): aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 2447, in wsgi_app aws-mwaa-local-runner-2_2-local-runner-1 | response = self.full_dispatch_request() aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1952, in full_dispatch_request aws-mwaa-local-runner-2_2-local-runner-1 | rv = self.handle_user_exception(e) aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1821, in handle_user_exception aws-mwaa-local-runner-2_2-local-runner-1 | reraise(exc_type, exc_value, tb) aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise aws-mwaa-local-runner-2_2-local-runner-1 | raise value aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1950, in full_dispatch_request aws-mwaa-local-runner-2_2-local-runner-1 | rv = self.dispatch_request() aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib/python3.7/site-packages/flask/app.py", line 1936, in dispatch_request aws-mwaa-local-runner-2_2-local-runner-1 | return self.view_functions[rule.endpoint](**req.view_args) aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib/python3.7/site-packages/flask_appbuilder/security/decorators.py", line 109, in wraps aws-mwaa-local-runner-2_2-local-runner-1 | return f(self, *args, **kwargs) aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib/python3.7/site-packages/flask_appbuilder/views.py", line 554, in list aws-mwaa-local-runner-2_2-local-runner-1 | widgets = self._list() aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib/python3.7/site-packages/flask_appbuilder/baseviews.py", line 1134, in _list aws-mwaa-local-runner-2_2-local-runner-1 | page_size=page_size, aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib/python3.7/site-packages/flask_appbuilder/baseviews.py", line 1033, in _get_list_widget aws-mwaa-local-runner-2_2-local-runner-1 | page_size=page_size, aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib/python3.7/site-packages/flask_appbuilder/models/sqla/interface.py", line 435, in query aws-mwaa-local-runner-2_2-local-runner-1 | query_results = query.all() aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib64/python3.7/site-packages/sqlalchemy/orm/query.py", line 3373, in all aws-mwaa-local-runner-2_2-local-runner-1 | return list(self) aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib64/python3.7/site-packages/sqlalchemy/orm/loading.py", line 100, in instances aws-mwaa-local-runner-2_2-local-runner-1 | cursor.close() aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib64/python3.7/site-packages/sqlalchemy/util/langhelpers.py", line 70, in __exit__ aws-mwaa-local-runner-2_2-local-runner-1 | with_traceback=exc_tb, aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib64/python3.7/site-packages/sqlalchemy/util/compat.py", line 182, in raise_ aws-mwaa-local-runner-2_2-local-runner-1 | raise exception aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib64/python3.7/site-packages/sqlalchemy/orm/loading.py", line 80, in instances aws-mwaa-local-runner-2_2-local-runner-1 | rows = [proc(row) for row in fetch] aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib64/python3.7/site-packages/sqlalchemy/orm/loading.py", line 80, in <listcomp> aws-mwaa-local-runner-2_2-local-runner-1 | rows = [proc(row) for row in fetch] aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib64/python3.7/site-packages/sqlalchemy/orm/loading.py", line 601, in _instance aws-mwaa-local-runner-2_2-local-runner-1 | state.manager.dispatch.load(state, context) aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib64/python3.7/site-packages/sqlalchemy/event/attr.py", line 322, in __call__ aws-mwaa-local-runner-2_2-local-runner-1 | fn(*args, **kw) aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib64/python3.7/site-packages/sqlalchemy/orm/mapper.py", line 3397, in _event_on_load aws-mwaa-local-runner-2_2-local-runner-1 | instrumenting_mapper._reconstructor(state.obj()) aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib/python3.7/site-packages/airflow/models/connection.py", line 153, in on_db_load aws-mwaa-local-runner-2_2-local-runner-1 | if self.password: aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib64/python3.7/site-packages/sqlalchemy/orm/attributes.py", line 365, in __get__ aws-mwaa-local-runner-2_2-local-runner-1 | retval = self.descriptor.__get__(instance, owner) aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib/python3.7/site-packages/airflow/models/connection.py", line 238, in get_password aws-mwaa-local-runner-2_2-local-runner-1 | return fernet.decrypt(bytes(self._password, 'utf-8')).decode() aws-mwaa-local-runner-2_2-local-runner-1 | File "/usr/local/lib64/python3.7/site-packages/cryptography/fernet.py", line 194, in decrypt aws-mwaa-local-runner-2_2-local-runner-1 | raise InvalidToken aws-mwaa-local-runner-2_2-local-runner-1 | cryptography.fernet.InvalidToken aws-mwaa-local-runner-2_2-local-runner-1 | 172.20.0.1 - - [29/Sep/2022:02:44:58 +0000] "GET /connection/list/ HTTP/1.1" 500 1538 "http://localhost:8080/configuration" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36"
コンテナ内で airflow connections list
コマンドを実行しても、エラーが返ってきます。
$ docker exec aws-mwaa-local-runner-local-runner-1 airflow connections list Traceback (most recent call last): File "/usr/local/bin/airflow", line 5, in <module> from airflow.__main__ import main File "/usr/local/lib/python3.7/site-packages/airflow/__init__.py", line 34, in <module> from airflow import settings File "/usr/local/lib/python3.7/site-packages/airflow/settings.py", line 37, in <module> from airflow.configuration import AIRFLOW_HOME, WEBSERVER_CONFIG, conf # NOQA F401 File "/usr/local/lib/python3.7/site-packages/airflow/configuration.py", line 1642, in <module> conf.validate() File "/usr/local/lib/python3.7/site-packages/airflow/configuration.py", line 321, in validate self._validate_config_dependencies() File "/usr/local/lib/python3.7/site-packages/airflow/configuration.py", line 429, in _validate_config_dependencies f"error: sqlite C library version too old (< {min_sqlite_version_str}). " airflow.exceptions.AirflowConfigException: error: sqlite C library version too old (< 3.15.0). See https://airflow.apache.org/docs/apache-airflow/2.4.0/howto/set-up-database.html#setting-up-a-sqlite-database
このエラー文ではSQLiteのバージョンが古いと言っています。
しかしaws-mwaa-local-runner はデフォルトではSQLiteを使わず、PostgreSQLを利用しています。
DBの設定は変更していなかったため、他に問題箇所がありそうでした。
エラー原因
実はこのエラーが発生する前後で./mwaa-local-env build-image
を実行して、AirflowのDockerイメージをビルドし直していました。これが今回のエラーの原因でした。
Airflowは接続情報や変数情報を暗号化してDBに保存します。
aws-mwaa-local-runner は、デフォルトではイメージのビルド時に暗号化キーを生成し、アプリケーションで使用します。このため、イメージをビルドし直すと暗号化キーが変わってしまい、DBに保存された値を正常に復号できずにエラーが発生しているようでした。
docker-compose-local.yml
ファイルを見るとわかりますが、DBのデータは ./db-data
ディレクトリにマウントされているため、DBコンテナを再作成しても保存されていた暗号化データはそのままです。
解消方法
データベースを初期化し、前の暗号化キーで作成された情報を削除すれば解消できます。
データベースの初期化方法は2通りです。
./mwaa-local-env reset-db
を実行する
./mwaa-local-env start
で起動したコンテナを一度停止し、./mwaa-local-env reset-db
でDBを初期化します。
その後停止したコンテナを再開させます。-
DBコンテナのマウントディレクトリを削除する
コンテナを削除し、db-dataディレクトリを削除します。$ docker rm aws-mwaa-local-runner-local-runner-1 aws-mwaa-local-runner-postgres-1 $ rm -rf db-data
その後再度
./mwaa-local-env start
でコンテナを作成します。
なお、DBを初期化したくない場合は、コンテナ起動時に元の暗号化キーを指定するといった手法も存在します。
Airflowコンテナの起動時、環境変数 FERNET_KEY
に暗号化キーを指定すれば、そのキーを使用してくれます。./mwaa-local-env start
コマンドは docker-compose-local.yml
ファイルからコンテナを起動しているので、docker-compose-local.yml
の local-runner
の定義で環境変数にFERNET_KEY
を追加します。
environment: - LOAD_EX=n - EXECUTOR=Local - FERNET_KEY=xxxxxxxxxxxxxxxxxxxxx
元の暗号化キーがわかる場合はこちらを利用しても良いでしょう。
余談
Airflow CLIを実行した際の sqlite C library version too old
というエラーは別の問題でした。
AirflowのDBへの接続情報は airflow.cfg
ファイル、もしくは環境変数 AIRFLOW__CORE__SQL_ALCHEMY_CONN
で指定できます。1 何も指定しない場合はSQLiteを使用するようになっているため、Airflow CLIを実行した際はSQLiteに接続を行い、エラーが発生していました。なお、Airflowアプリケーションを立ち上げた際は [entrypoint.sh](http://entrypoint.sh)
ファイルで環境変数 AIRFLOW__CORE__SQL_ALCHEMY_CONN
を指定してアプリケーションを起動しているため、SQLiteではなくPostgreSQLを使うようになっています。
こちらのエラーの対処法としては、Airflow CLIの実行時もしくはコンテナ自体に環境変数 AIRFLOW__CORE__SQL_ALCHEMY_CONN
を指定し、PostgreSQLに接続するようにします。方法は以下の3通りです。
- コンテナの立ち上げ時に環境変数を指定する
docker/docker-compose-local.yml
でlocal-runner
コンテナの環境変数を指定します。local-runner: image: amazon/mwaa-local:2.0 restart: always depends_on: - postgres environment: - LOAD_EX=n - EXECUTOR=Local - AIRFLOW__CORE__SQL_ALCHEMY_CONN=postgresql+psycopg2://airflow:airflow@postgres:5432/airflow
-
Airflow CLI実行時に環境変数を指定する
$ docker exec aws-mwaa-local-runner-2_2-local-runner-1 /bin/bash -c "AIRFLOW__CORE__SQL_ALCHEMY_CONN=postgresql+psycopg2://airflow:airflow@postgres:5432/airflow airflow version"
-
entrypoint.sh
からコマンドを実行する
Airflowアプリケーションの立ち上げと同様、entrypoint.sh
を経由してコマンドを実行します。
entrypoint.sh
では環境変数AIRFLOW__CORE__SQL_ALCHEMY_CONN
が空の場合、PostgreSQLへの接続情報をAIRFLOW__CORE__SQL_ALCHEMY_CONN
に代入するようになっています。$ docker exec aws-mwaa-local-runner-2_2-local-runner-1 /bin/bash -c "/entrypoint.sh airflow version"
参考リンク
Apache Airflow Fernet
https://github.com/aws/aws-mwaa-local-runner/issues/19